package nape.shape;
import zpp_nape.Config;
import zpp_nape.Const;
import zpp_nape.util.Flags;
import DummyNapeMain;
import zpp_nape.util.Math;
import zpp_nape.util.Names;
import zpp_nape.util.Pool;
import zpp_nape.util.Lists;
import zpp_nape.shape.Circle;
import zpp_nape.shape.Edge;
import zpp_nape.space.Space;
import zpp_nape.shape.Polygon;
import zpp_nape.shape.Shape;
import zpp_nape.phys.FeatureMix;
import zpp_nape.phys.FluidProperties;
import zpp_nape.phys.Material;
import zpp_nape.phys.Body;
import zpp_nape.geom.AABB;
import zpp_nape.geom.Distance;
import zpp_nape.geom.Mat23;
import zpp_nape.geom.Vec2;
import zpp_nape.geom.VecMath;
import zpp_nape.geom.Collide;
import zpp_nape.dynamics.Arbiter;
import zpp_nape.dynamics.Contact;
import zpp_nape.dynamics.InteractionFilter;
import zpp_nape.constraint.Constraint;
import zpp_nape.dynamics.SpaceArbiterList;
import zpp_nape.constraint.Matrices;
import zpp_nape.constraint.PivotJoint;
import zpp_nape.constraint.WeldJoint;
import nape.util.Flags;
import nape.util.Lists;
import nape.util.Debug;
import nape.space.Space;
import nape.shape.Circle;
import nape.shape.Polygon;
import nape.shape.Shape;
import nape.phys.FluidProperties;
import nape.phys.Material;
import nape.phys.Body;
import nape.geom.AABB;
import nape.geom.Geom;
import nape.geom.Mat23;
import nape.geom.Vec2;
import nape.dynamics.Contact;
import nape.dynamics.Arbiter;
import nape.dynamics.InteractionFilter;
#if swc@:keep#end class EdgeList{
    public var zpp_inner:ZPP_EdgeList;
    public var length(flibget_length,never):Int;
    #if true inline function flibget_length(){
        zpp_inner.valmod();
        if(zpp_inner.zip_length){
            zpp_inner.zip_length=false;
            if(false){
                zpp_inner.user_length=0;
                {
                    var cx_ite=zpp_inner.inner.begin();
                    while(cx_ite!=null){
                        var i=cx_ite.elem();
                        if(true)zpp_inner.user_length++;
                        cx_ite=cx_ite.next;
                    }
                };
            }
            else zpp_inner.user_length=zpp_inner.length;
        }
        return zpp_inner.user_length;
    }
    #else inline function flibget_length(){
        return zpp_gl();
    }
    public function zpp_gl(){
        zpp_inner.valmod();
        if(zpp_inner.zip_length){
            zpp_inner.zip_length=false;
            if(false){
                zpp_inner.user_length=0;
                {
                    var cx_ite=zpp_inner.inner.begin();
                    while(cx_ite!=null){
                        var i=cx_ite.elem();
                        if(true)zpp_inner.user_length++;
                        cx_ite=cx_ite.next;
                    }
                };
            }
            else zpp_inner.user_length=zpp_inner.length;
        }
        return zpp_inner.user_length;
    }
    public function zpp_vm(){
        zpp_inner.valmod();
    }
    #end#if swc@:keep#end public static inline function fromArray(array:#if flash9 Array<Dynamic>#else Array<Edge>#end){
        var ret=new EdgeList();
        for(i in array){
            #if flash9 if(!Std.is(i,Edge))throw "Error: Array contains non "+"Edge"+" types.";
            #end ret.push(i);
        }
        return ret;
    }
    #if flash9#if swc@:keep#end public static inline function fromVector(vector:flash.Vector<Edge>){
        var ret=new EdgeList();
        for(i in vector)ret.push(i);
        return ret;
    }
    #end#if swc@:keep#end public inline function at(index:Int):Edge{
        #if true zpp_inner.valmod();
        #else zpp_vm();
        #end if(index<0||index>=length)throw "Error: Index out of bounds";
        if(zpp_inner.reverse_flag)index=(index==length-1?index:length-2-index);
        if(!false){
            if(index<zpp_inner.at_index||zpp_inner.at_ite==null){
                zpp_inner.at_index=index;
                zpp_inner.at_ite=zpp_inner.inner.iterator_at(index);
            }
            else{
                while(zpp_inner.at_index!=index){
                    zpp_inner.at_index++;
                    zpp_inner.at_ite=zpp_inner.at_ite.next;
                }
            }
        }
        else{
            if(index<zpp_inner.at_index||zpp_inner.at_ite==null){
                zpp_inner.at_index=0;
                zpp_inner.at_ite=zpp_inner.inner.begin();
                while(true){
                    var x=zpp_inner.at_ite.elem();
                    if(true)break;
                    zpp_inner.at_ite=zpp_inner.at_ite.next;
                }
            }
            while(zpp_inner.at_index!=index){
                zpp_inner.at_index++;
                zpp_inner.at_ite=zpp_inner.at_ite.next;
                while(true){
                    var x=zpp_inner.at_ite.elem();
                    if(true)break;
                    zpp_inner.at_ite=zpp_inner.at_ite.next;
                }
            }
        }
        return zpp_inner.at_ite.elem().wrapper();
    }
    #if swc@:keep#end public inline function push(obj:Edge){
        if(zpp_inner.immutable)throw "Error: "+"Edge"+"List is immutable";
        zpp_inner.modify_test();
        #if true zpp_inner.valmod();
        #else zpp_vm();
        #end var cont=if(zpp_inner.adder!=null)zpp_inner.adder(obj)else true;
        if(cont){
            if(zpp_inner.reverse_flag)zpp_inner.inner.add(obj.zpp_inner);
            else{
                if(zpp_inner.push_ite==null)zpp_inner.push_ite=empty()?null:zpp_inner.inner.iterator_at(length-1);
                zpp_inner.push_ite=zpp_inner.inner.insert(zpp_inner.push_ite,obj.zpp_inner);
            }
            zpp_inner.invalidate();
            if(zpp_inner.post_adder!=null)zpp_inner.post_adder(obj);
        }
        return cont;
    }
    #if swc@:keep#end public inline function unshift(obj:Edge){
        if(zpp_inner.immutable)throw "Error: "+"Edge"+"List is immutable";
        zpp_inner.modify_test();
        #if true zpp_inner.valmod();
        #else zpp_vm();
        #end var cont=if(zpp_inner.adder!=null)zpp_inner.adder(obj)else true;
        if(cont){
            if(zpp_inner.reverse_flag){
                if(zpp_inner.push_ite==null)zpp_inner.push_ite=empty()?null:zpp_inner.inner.iterator_at(length-1);
                zpp_inner.push_ite=zpp_inner.inner.insert(zpp_inner.push_ite,obj.zpp_inner);
            }
            else zpp_inner.inner.add(obj.zpp_inner);
            zpp_inner.invalidate();
            if(zpp_inner.post_adder!=null)zpp_inner.post_adder(obj);
        }
        return cont;
    }
    #if swc@:keep#end public inline function pop():Edge{
        if(zpp_inner.immutable)throw "Error: "+"Edge"+"List is immutable";
        zpp_inner.modify_test();
        if(empty())throw "Error: Cannot remove from empty list";
        #if true zpp_inner.valmod();
        #else zpp_vm();
        #end var ret=null;
        if(zpp_inner.reverse_flag){
            ret=zpp_inner.inner.front();
            var retx=ret.wrapper();
            if(zpp_inner.subber!=null)zpp_inner.subber(retx);
            zpp_inner.inner.pop();
        }
        else{
            if(zpp_inner.at_ite!=null&&zpp_inner.at_ite.next==null)zpp_inner.at_ite=null;
            var ite=length==1?null:zpp_inner.inner.iterator_at(length-2);
            ret=ite==null?zpp_inner.inner.front():ite.next.elem();
            var retx=ret.wrapper();
            if(zpp_inner.subber!=null)zpp_inner.subber(retx);
            zpp_inner.inner.erase(ite);
        }
        zpp_inner.invalidate();
        var retx=ret.wrapper();
        return retx;
    }
    #if swc@:keep#end public inline function shift():Edge{
        if(zpp_inner.immutable)throw "Error: "+"Edge"+"List is immutable";
        zpp_inner.modify_test();
        if(empty())throw "Error: Cannot remove from empty list";
        #if true zpp_inner.valmod();
        #else zpp_vm();
        #end var ret=null;
        if(zpp_inner.reverse_flag){
            if(zpp_inner.at_ite!=null&&zpp_inner.at_ite.next==null)zpp_inner.at_ite=null;
            var ite=length==1?null:zpp_inner.inner.iterator_at(length-2);
            ret=ite==null?zpp_inner.inner.front():ite.next.elem();
            var retx=ret.wrapper();
            if(zpp_inner.subber!=null)zpp_inner.subber(retx);
            zpp_inner.inner.erase(ite);
        }
        else{
            ret=zpp_inner.inner.front();
            var retx=ret.wrapper();
            if(zpp_inner.subber!=null)zpp_inner.subber(retx);
            zpp_inner.inner.pop();
        }
        zpp_inner.invalidate();
        var retx=ret.wrapper();
        return retx;
    }
    #if swc@:keep#end public inline function add(obj:Edge)return if(zpp_inner.reverse_flag)push(obj)else unshift(obj)#if swc@:keep#end public inline function remove(obj:Edge):Bool{
        if(zpp_inner.immutable)throw "Error: "+"Edge"+"List is immutable";
        zpp_inner.modify_test();
        #if true zpp_inner.valmod();
        #else zpp_vm();
        #end var ret=({
            var ret=false;
            {
                var cx_ite=zpp_inner.inner.begin();
                while(cx_ite!=null){
                    var x=cx_ite.elem();
                    {
                        if(x==obj.zpp_inner){
                            ret=true;
                            break;
                        }
                    };
                    cx_ite=cx_ite.next;
                }
            };
            ret;
        });
        if(ret){
            if(zpp_inner.subber!=null)zpp_inner.subber(obj);
            zpp_inner.inner.remove(obj.zpp_inner);
            zpp_inner.invalidate();
        }
        return ret;
    }
    #if swc@:keep#end public inline function empty(){
        #if true if(false)return length==0;
        else return zpp_inner.inner.empty();
        #else return length==0;
        #end
    }
    #if swc@:keep#end public inline function iterator(){
        #if true zpp_inner.valmod();
        #else zpp_vm();
        #end if(zpp_inner.iterators==null)zpp_inner.iterators=new ZNPList_EdgeIterator();
        return EdgeIterator.get(this);
    }
    #if swc@:keep#end public inline function copy(?deep:Bool=false){
        var ret=new EdgeList();
        for(i in this)ret.push(deep?{
            throw "Error: "+"Edge"+" is not a copyable type";
        }
        :i);
        return ret;
    }
    public function new(){
        zpp_inner=new ZPP_EdgeList();
        zpp_inner.outer=this;
    }
    @:keep public inline function toString(){
        var ret="[";
        var fst=true;
        for(i in this){
            if(!fst)ret+=",";
            ret+=i.toString();
            fst=false;
        }
        return ret+"]";
    }
}
#if swc@:keep#end class EdgeIterator{
    public var zpp_inner:EdgeList;
    public var zpp_i:Int;
    public var zpp_critical:Bool;
    public static var zpp_pool:EdgeIterator;
    public var zpp_next:EdgeIterator;
    public function new(){
        if(!ZPP_EdgeList.internal)throw "Error: Cannot instantiate "+"Edge"+"Iterator derp!";
    }
    public static inline function get(list:EdgeList){
        var ret=if(zpp_pool==null){
            ZPP_EdgeList.internal=true;
            var ret=new EdgeIterator();
            ZPP_EdgeList.internal=false;
            ret;
        }
        else{
            var r=zpp_pool;
            zpp_pool=r.zpp_next;
            r;
        }
        ret.zpp_i=0;
        ret.zpp_inner=list;
        ret.zpp_critical=false;
        list.zpp_inner.iterators.add(ret);
        return ret;
    }
    #if swc@:keep#end public inline function hasNext(){
        #if true zpp_inner.zpp_inner.valmod();
        #else zpp_inner.zpp_vm();
        #end var length=zpp_inner.length;
        zpp_critical=true;
        if(zpp_i<length){
            return true;
        }
        else{
            zpp_next=zpp_pool;
            zpp_pool=this;
            zpp_inner.zpp_inner.iterators.remove(this);
            return false;
        }
    }
    #if swc@:keep#end public inline function next(){
        zpp_critical=false;
        return zpp_inner.at(zpp_i++);
    }
}
#if swc@:keep#end class Edge{
    public var zpp_inner:ZPP_Edge;
    public function new(){
        if(!ZPP_Edge.internal)throw "Error: Cannot instantiate an Edge derp!";
    }
    public var localNormal(flibget_localNormal,never):Vec2;
    public var worldNormal(flibget_worldNormal,never):Vec2;
    inline function flibget_localNormal(){
        if(zpp_inner.polygon==null)throw "Error: Edge not currently in use";
        if(zpp_inner.wrap_lnorm==null)zpp_inner.get_lnorm();
        return zpp_inner.wrap_lnorm;
    }
    inline function flibget_worldNormal(){
        if(zpp_inner.polygon==null)throw "Error: Edge not currently in use";
        if(zpp_inner.wrap_gnorm==null)zpp_inner.get_gnorm();
        return zpp_inner.wrap_gnorm;
    }
    public var length(flibget_length,never):Float;
    inline function flibget_length(){
        if(zpp_inner.polygon==null)throw "Error: Edge not currently in use";
        zpp_inner.polygon.validate_laxi();
        return zpp_inner.length;
    }
    public var localProjection(flibget_localProjection,never):Float;
    inline function flibget_localProjection(){
        if(zpp_inner.polygon==null)throw "Error: Edge not current in use";
        zpp_inner.polygon.validate_laxi();
        return zpp_inner.lprojection;
    }
    public var worldProjection(flibget_worldProjection,never):Float;
    inline function flibget_worldProjection(){
        if(zpp_inner.polygon==null)throw "Error: Edge not current in use";
        if(zpp_inner.polygon.body==null)throw "Error: Edge world projection only makes sense for Polygons contained within a rigid body";
        zpp_inner.polygon.validate_gaxi();
        return zpp_inner.gprojection;
    }
    @:keep public inline function toString(){
        if(zpp_inner.polygon==null)return "Edge(object-pooled)";
        else if(zpp_inner.polygon.body==null){
            zpp_inner.polygon.validate_laxi();
            return "{ localNormal : "+("{ x: "+zpp_inner.lnormx+" y: "+zpp_inner.lnormy+" }")+" }";
        }
        else{
            zpp_inner.polygon.validate_gaxi();
            return "{ localNormal : "+("{ x: "+zpp_inner.lnormx+" y: "+zpp_inner.lnormy+" }")+" worldNormal : "+("{ x: "+zpp_inner.gnormx+" y: "+zpp_inner.gnormy+" }")+" }";
        }
    }
}
